/**
 * @author Francesco di Dio
 * Date: 21/apr/2013
 * Titolo: JKUtils.java
 * Versione: 
 */

/**
 * Copyright (c) 2013 Francesco di Dio.
 * tabuto83@gmail.com 
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package com.tabuto.jkeys;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.log4j.Logger;

import com.tabuto.jkeys.controllers.AbstractJKAction;
import com.tabuto.jkeys.models.AccountGroupList;

/**
 * 
 */
public class JKUtils {
	
	private static final Logger log = Logger.getLogger(JKUtils.class);

	public static JButton makeNavigationButton(String imageName,
			AbstractJKAction actionCommand, String toolTipText, String altText) {
		// Look for the image.
		String imgLocation = JKeysConstant.ICONS_PATH + imageName
				+ Messages.getString("JKeys.app_icons_ext");

		URL imageURL = JKeys.class.getResource(imgLocation);

		// Crea ed inizializza il jbutton.
		JButton button = new JButton();
		button.setToolTipText(toolTipText); // Aggiungo il testo da mostrare al
											// passaggio del mouse
		button.addActionListener(actionCommand); // Aggiungo l'azione passata
													// come parametro
		// Restituisce il button correttamente valorizzato

		if (imageURL != null) { // image found
			button.setIcon(new ImageIcon(imageURL, altText));
		} else { // no image found
			button.setText(altText);
			System.err.println(Messages
					.getString("JKeys.msg.resource_not_found") + imgLocation);
		}

		return button;

	}

	// TODO: should be use Generics
	public static void saveDataToXML(AccountGroupList toSave, String filePath)
			throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(AccountGroupList.class);
		Marshaller m = context.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		m.marshal(toSave, System.out);
		m.marshal(toSave, new File(filePath));
	}

	/**
	 * Istanzia un nuovo {@linkplain GameManager} a partire da un file xml che
	 * descrive un oggetto {@linkplain GameData}
	 * 
	 * @param xmlPath
	 *            Il percorso del file xml
	 * @return {@linkplain GameManager}
	 * @throws JAXBException
	 * @throws FileNotFoundException
	 */
	public static AccountGroupList loadGameData(String filePath)
			throws JAXBException, FileNotFoundException {
		/*
		 * Carico la classe usando la libreria nativa JAXB
		 */
		JAXBContext context = JAXBContext.newInstance(AccountGroupList.class);
		Unmarshaller um = context.createUnmarshaller();

		AccountGroupList data = (AccountGroupList) um.unmarshal(JKeys.class
				.getResourceAsStream(filePath));

		return data;
	}

	public static void showMessage(String message) {
		if (message != null && !message.equals(""))
			JOptionPane.showMessageDialog(null, message);
	}

	public static boolean isEmptyOrNullString(String toEval) {
		return (toEval == null || toEval.equals(""));
	}

	public static String showInputPasswordDialog() {
		log.info("showInputPasswordDialog");
		JPanel panel = new JPanel();
		JLabel label = new JLabel("Enter a password:");
		JPasswordField pass = new JPasswordField(10);
		panel.add(label);
		panel.add(pass);
		String[] options = new String[] { "OK", "Cancel" };
		int option = JOptionPane.showOptionDialog(null, panel, "The title",
				JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null,
				options, options[1]);
		if (option == 0) // pressing OK button
		{
			char[] password = pass.getPassword();
			return new String(password);
		} else {
			return null;
		}
	}

	public static String showSetPasswordDialog() {
		log.info("showSetPasswordDialog");
		JPanel panel = new JPanel();
		JLabel label1 = new JLabel("Enter a password:");
		JPasswordField pass = new JPasswordField(10);
		JLabel label2 = new JLabel("Repeat password:");
		JPasswordField pass2 = new JPasswordField(10);
		panel.add(label1);
		panel.add(pass);
		panel.add(label2);
		panel.add(pass2);
		String[] options = new String[] { "OK", "Cancel" };
		
		int option = JOptionPane.showOptionDialog(null, panel,
				"Insert Password", JOptionPane.NO_OPTION,
				JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
		if (option == 0) // pressing OK button
		{
			String password1 = new String(pass.getPassword());
			String password2 = new String(pass2.getPassword());

			if (JKUtils.isEmptyOrNullString(password1)
					|| JKUtils.isEmptyOrNullString(password2)
					|| !password1.equals(password2)) {
				JOptionPane.showMessageDialog(null,
						"The password doesn't match!");

				return null;
			} else {
				JOptionPane.showMessageDialog(null,
						"Done! Don't forgive your password!");
			}

			return password1;
		} else {
			return null;
		}
	}

	// TODO: Manage with exception
	public int validatePassword(String pass1, String repeat, int minLenght) {

		int falseValue = -1;
		int trueValue = 1;

		if (JKUtils.isEmptyOrNullString(pass1)
				|| JKUtils.isEmptyOrNullString(repeat) || !pass1.equals(repeat)) {
			JOptionPane.showMessageDialog(null, "The password doesn't match!");

			return falseValue;
		}

		if (pass1.length() < minLenght)
			return falseValue;

		// TODO: evaluate password complexity and return a positive integer as
		// score!

		JOptionPane.showMessageDialog(null,
				"Done! Don't forgive your password!");
		return trueValue;

	}
	
	private static Cipher getCipher(String password, int encryptMode) {
		log.info("getCipher");
		try{
		/* Derive the key, given password and salt. */
		byte[] salt = new byte[] { (byte) 0x8F, 0x12, 0x39, (byte) 0x9E,
				0x06, 0x71, 0x6F, 0x4A};
		SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
		KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
		SecretKey tmp = factory.generateSecret(spec);
		SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
		/* Encrypt the message. */
		Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
		cipher.init(encryptMode, secret);
		return cipher;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException("An application error was found! " + ex);
		} 
		
	}
	
	

	public static void encrypt(InputStream clearIn, OutputStream cipherOut,
			String password) throws IOException  {

			log.info("encrypt");


			byte[] buf = new byte[1024];
			// INITIALIZE CIPHER
			// Create a 16-byte initialization vector
//			byte[] ivx = new byte[] { (byte) 0x8F, 0x12, 0x39, (byte) 0x9E,
//					0x06, 0x71, 0x6F, 0x4A,  (byte) 0x1F, (byte) 0xCC, 0x39, (byte) 0x9E,
//					(byte) 0xA6, 0x16, 0x1E, 0x4A  };			
//			SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(),
//					"AES");
//			IvParameterSpec ivSpec = new IvParameterSpec(ivx);
//			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//			cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
			Cipher cipher = getCipher(password, Cipher.ENCRYPT_MODE);
			//READ AND WRITE
			cipherOut = new CipherOutputStream(cipherOut, cipher);
			int numRead = 0;
			while ((numRead = clearIn.read(buf)) >= 0) {
				cipherOut.write(buf, 0, numRead);
			}
			cipherOut.close();



	}

	public static void decrypt(InputStream cipherIn, OutputStream clearOut,
			String password) throws IOException {
	//	try {
		log.info("decrypt");
			byte[] buf = new byte[1024];
			// INITIALIZE CIPHER
			// Create a 16-byte initialization vector
//			byte[] ivx = new byte[] { (byte) 0x8F, 0x12, 0x39, (byte) 0x9E,
//					0x06, 0x71, 0x6F, 0x4A,  (byte) 0x1F, (byte) 0xCC, 0x39, (byte) 0x9E,
//					(byte) 0xA6, 0x16, 0x1E, 0x4A  };			
//			SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(),
//					"AES");
//			IvParameterSpec ivSpec = new IvParameterSpec(ivx);
//			Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//			decipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

			Cipher decipher = getCipher(password, Cipher.DECRYPT_MODE);
			//READ AND WRITE
			cipherIn = new CipherInputStream(cipherIn, decipher);

			// Read in the decrypted bytes and write the cleartext to out
			int numRead = 0;
			while ((numRead = cipherIn.read(buf)) >= 0) {
				clearOut.write(buf, 0, numRead);
			}
			clearOut.close();

//		} catch (Exception e) {
//			e.printStackTrace();
//		}
	}

	private JKUtils() {
	}

}
